/*
*  Copyright Nate Dierk 2013
*/

#ifndef GEdge_Defined
#define GEdge_Defined

#include <algorithm>

#include "GBitmap.h"
#include "GPoint.h"
#include "GTypes.h"
#include "GMatrix_nd.h"


///////////////////////////////////////////////////////////
// GEdge Class

class GEdge {
protected:
	// evaluates an edge for a given Y
	inline float evalY(const GPoint* p){
		return p->fX + ((float) fCurY - p->fY) * fSlope;
	}

	// points coming in are sorted such that p1.fY < p2.fY
	void init(const GPoint* p1, const GPoint* p2, int maxRow){
		fSlope = (p2->fX - p1->fX) / (p2->fY - p1->fY);
		fCurY = std::max(0, (int) ceilf(p1->fY)); // round to NEXT row (and clip top)
		
		fCurX = p1->fX + ((float) fCurY - p1->fY) * fSlope;
		fRowsLeft = std::min(maxRow, (int) ceilf(p2->fY)) - fCurY; // last row center - first row center
	}

public:
	float fCurX, fSlope;
	int fCurY, fRowsLeft;

	// points are coming in unsorted
	void set(const GPoint* p1, const GPoint* p2, int maxRow){
		if (p1->fY < p2->fY){ init(p1, p2, maxRow); }
		else { init(p2, p1, maxRow); }
	}

	int getStart(){
		return std::max((int) ceilf(fCurX), 0);
	}

	void step(){
		fCurX += fSlope;
		++fCurY;
		--fRowsLeft;
	}

	static bool comp(const GEdge e1, const GEdge e2){ // sort by asc. Y, then by asc. X
		if (e1.fCurY == e2.fCurY){ return e1.fCurX < e2.fCurX; } 
		else { return e1.fCurY < e2.fCurY; }
	}

	void print(){
		printf("EDGE x:%f, y:%d, slope:%f, rows:%d\n",
			    fCurX, fCurY, fSlope, fRowsLeft);
	}
};

// checks that edge intersects bitmap and isn't horizontal
static bool validEdge(const GPoint* p1, const GPoint* p2, int height){
    	if (ceilf(p1->fY) == ceilf(p2->fY)) { return false; }        //crosses no pixel centers
    	if ((p1->fY < 0.0f)   && (p2->fY < 0.0f))   { return false; } // above
    	if ((p1->fY > height) && (p2->fY > height)) { return false; } // below
    	return true;
}

////////////////////////////////////////////////////////////
// Line creation function - given points it transforms them into device space
// and creates all line pairings linearly (ie pt 1 pairs with pt 0 and 2, etc.)

static int makeXformedEdges(const GBitmap* bmp, const GMatrix* ctm,
	                         const GPoint* points, int numPoints, GEdge* edges){
	GPoint lastP = ctm->toDeviceCoords(points[numPoints-1]);

	int	edgeCount = 0, maxRow = bmp->fHeight;
	for (int i = 0; i < numPoints; ++i){
		GPoint p = ctm->toDeviceCoords(points[i]);

		if (validEdge(&p, &lastP, bmp->height())){
			edges[edgeCount++].set(&lastP, &p, maxRow);
		}
		lastP = p;
	}

	std::sort(edges, edges + edgeCount, GEdge::comp);
	return edgeCount;
}

#endif